//
// Created by yunnan xu on 2020/11/21.
//

#include <ext.hpp>
#include <cmath>
#include "Camera.h"
#include "MobileInput.h"
#include "EngineCore.h"
#include "Scene.h"


Camera::Camera(float z_near, float z_far, float aspect_ratio, float _fov): zNear(z_near), zFar(z_far), aspectRatio(aspect_ratio), fov(_fov)
{
    position = glm::vec3(0, 0, 20);
    up = glm::vec3(0, 1, 0);
    forward = glm::vec3(0, 0, -1);
    right = glm::cross(forward, up);
    projMatrix = glm::perspective(fov, aspectRatio, zNear, zFar);
    UpdateMatrix();

    lastCenterX = -1.0f;
    lastCenterY = -1.0f;
}

void Camera::SetOnMobileInput()
{
    MobileInputMgr::GetInstance()->SetOnDiffX([](float diffx){

        // LOGI("Camera diffx = %f", diffx);

        auto main_cam = SceneMgr::GetInstance()->GetCurrentScene()->GetMainCamera();
        auto now_forward = main_cam->GetForward();
        float theta = diffx * 0.002f;
        glm::vec3 target_forward = glm::vec3(
                now_forward.x * cos(theta) - now_forward.z * sin(theta),
                now_forward.y,
                now_forward.x * sin(theta) + now_forward.z * cos(theta));
        main_cam -> SetForward(target_forward);
    });

    MobileInputMgr::GetInstance()->SetOnDiffY([](float diffy){

        // LOGI("Camera diffy = %f", diffy);

        auto main_cam = SceneMgr::GetInstance()->GetCurrentScene()->GetMainCamera();
        main_cam -> SetPosition(main_cam->GetPosition() + diffy * main_cam->GetUp() * -0.02f);
    });

    MobileInputMgr::GetInstance()->SetOnZoom([](float centerx, float centery, float diff) {
        auto main_cam = SceneMgr::GetInstance()->GetCurrentScene()->GetMainCamera();

        float forward_scalr = log(diff);
        main_cam -> AddPositionVec(main_cam->GetForward()  * forward_scalr * 10.0f);

        // LOGI("forward_scalr = %f", forward_scalr);

        if (main_cam->lastCenterY > 0.0f && main_cam->lastCenterX > 0.0f && forward_scalr < 0.01f && forward_scalr > -0.01f)
        {
            float diffx = centerx - main_cam->lastCenterX;
            float diffy = centery - main_cam->lastCenterY;

            main_cam -> SetPosition(main_cam->GetPosition() + ( - main_cam->GetRight() * diffx  + main_cam->GetUp() * diffy) * -0.02f);
        }

        main_cam->lastCenterY = centery;
        main_cam->lastCenterX = centerx;

    });

    MobileInputMgr::GetInstance()->SetOnCancel([this](){
        lastCenterX = -1.0f;
        lastCenterY = -1.0f;
    });
}


glm::mat4x4 Camera::GetCameraTransform() const
{
    return glm::inverse(viewMatrix);
}

const glm::mat4x4 & Camera::GetViewMatrix() const
{
    return viewMatrix;
}

const glm::mat4x4 & Camera::GetProjMatrix() const
{
    return projMatrix;
}

glm::vec3 Camera::GetPosition () const
{
    return position;
}

glm::vec3 Camera::GetForward () const
{
    return forward;
}

glm::vec3 Camera::GetUp () const
{
    return up;
}

glm::vec3 Camera::GetRight () const
{
    return right;
}

void Camera::AddPositionVec(glm::vec3 add_vec)
{
    position += add_vec;
    UpdateMatrix();
}

void Camera::SetPosition(glm::vec3 pos_vec)
{
    position = pos_vec;
    UpdateMatrix();
}

void Camera::SetForward(glm::vec3 for_vec)
{
    forward = glm::normalize(for_vec);
    right = glm::cross(forward, up);
    UpdateMatrix();
}


void Camera::UpdateMatrix()
{
    viewMatrix = glm::lookAt(position, position + forward, up);
}
